/*
 * Diagram.cpp
 *
 *  Created on: 11.04.2010
 *      Author: king
 */

#include <QtGui>

#include "Diagram.h"
#include "Link.h"
#include "Node.h"
#include "AddPersonDialog.h"
#include "MainWindow.h"
#include "tree/TreeIterator.h"

namespace etu {
namespace ui {

using etu::tree::TreeIterator;
using etu::genealogy_tree::Photo;

static QColor FOUND = Qt::red;
static QColor NORMAL = Qt::darkBlue;
static const int DISTANCE = 150;

CDiagram::CDiagram(QWidget *parent): QGraphicsView(parent)
{
    m_scene = new QGraphicsScene(this);

    setScene(m_scene);
    setDragMode(QGraphicsView::RubberBandDrag);
    setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    setContextMenuPolicy(Qt::ActionsContextMenu);

    minZ = 0;
    maxZ = 0;
    seqNumber = 0;

    m_tree = new GenealogyTree();

    createActions();

    connect(m_scene, SIGNAL(selectionChanged()),
    		this , SLOT(updateActions()));

    updateActions();
}

CDiagram::~CDiagram()
{
  delete m_tree;
}

void CDiagram::init(etu::db::DataSource* dataSource)
{
  m_dataSource = dataSource;
}

GenealogyTree* CDiagram::getTree()
{
  return m_tree;
}

void CDiagram::setTree(GenealogyTree* tree)
{
  clear();
  m_tree = tree;
  replotTree();
}

void CDiagram::clear()
{
  if (m_scene->items().empty())
    return;

  m_scene->clear();
  if (m_tree != NULL)
    delete m_tree;
}

void CDiagram::replotTree()
{
  if (m_tree != NULL)
  {
    TreeIterator<const Person* >* it = m_tree->getIterator();
    //it->goToFirst();
    while(it->hasNext())
    {
      Person* person = const_cast<Person* >(it->next());
      if (person != NULL )
      {
    	bool isPainted = false;
	    CNode *first = new CNode(person);
	    foreach (CNode *second, getNodes(m_scene->items()))
	    {
	      int flag = m_tree->getState(first->getPerson(), second->getPerson());
	      if (flag & (BIT(etu::genealogy_tree::FATHER)
                   | BIT(etu::genealogy_tree::MOTHER)
                   | BIT(etu::genealogy_tree::CHILD)))
	      {
	    	createLink(first,second);
	    	if (!isPainted)
	        {
	           setupNode(first,second);
	           isPainted = true;
	        }
	      }
	    }
        if (!isPainted)
	      setupNode(first,NULL);
      }
    }
  }
}

void CDiagram::createLink(CNode* first, CNode* second)
{
    CLink *link = new CLink(first, second);
    m_scene->addItem(link);
}

void CDiagram::deletePerson()
{
	try
	{
	  m_tree->remove(selectedNode()->getPerson());
      emit deletePerson(true);
      removeDeletedPersons();
	}
	catch (genealogy_tree::GenealogyTreeException& ex) {
	  QMessageBox::critical(this,tr("Ошибка удаления персоны из дерева"),QString(tr(ex.what())));
	}
}

void CDiagram::bringToFront()
{
    ++maxZ;
    setZValue(maxZ);
}

void CDiagram::sendToBack()
{
    --minZ;
    setZValue(minZ);
}

void CDiagram::updateActions()
{
 try {
   bool isNodePair = (selectedNodePair() != NodePair());
   bool isNode = (selectedNode() != 0);

   int flag = isNode ? m_tree->getState(selectedNode()->getPerson()) : 0;
   int pairFlag = isNodePair ? m_tree->getState(selectedNodePair().first->getPerson(), selectedNodePair().second->getPerson()) : 0;

   addFatherAction->setEnabled(!(flag &BIT(etu::genealogy_tree::FATHER)) && isNode);
   addMotherAction->setEnabled(!(flag &BIT(etu::genealogy_tree::MOTHER)) && isNode);
   addChildAction->setEnabled((pairFlag &BIT(etu::genealogy_tree::MARRIED)) || isNode);

   deleteAction->setEnabled(!(flag & BIT(etu::genealogy_tree::ROOT)) && isNode);

   foreach (QAction *action, actions())
     removeAction(action);

   foreach (QAction *action, m_actions)
     if (action->isEnabled())
	   addAction(action);

   if (isNode)
     emit selectPerson(selectedNode()->getPerson(),selectedNode()->getPhoto());
  }
   catch (genealogy_tree::GenealogyTreeException& ex) {
	  QMessageBox::critical(this,"",QString(tr(ex.what())));
  }
}

void CDiagram::updateCurrentNode()
{
  if (selectedNode() != 0)
  {
	selectedNode()->setupPersonName();
    selectedNode()->update();
  }
}

void CDiagram::createActions()
{
    addFatherAction = new QAction(tr("Добавить Отца"),this);
    addFatherAction->setShortcut(tr("Ctrl+F"));
    connect(addFatherAction, SIGNAL(triggered()), this, SLOT(addFather()));
    m_actions.push_back(addFatherAction);

    addMotherAction = new QAction(tr("Добавить Maть"),this);
    addMotherAction->setShortcut(tr("Ctrl+M"));
    connect(addMotherAction, SIGNAL(triggered()), this, SLOT(addMother()));
    m_actions.push_back(addMotherAction);

    addChildAction = new QAction(tr("Добавить Потомка"),this);
    addChildAction->setShortcut(tr("Ctrl+P"));
    connect(addChildAction, SIGNAL(triggered()), this, SLOT(addChild()));
    m_actions.push_back(addChildAction);

    deleteAction = new QAction(tr("Удалить"),this);
    deleteAction->setShortcut(tr("Del"));
    connect(deleteAction, SIGNAL(triggered()), this, SLOT(deletePerson()));
    m_actions.push_back(deleteAction);
}

void CDiagram::setZValue(int z)
{
    CNode *node = selectedNode();
    if (node)
        node->setZValue(z);
}

void CDiagram::setupNode(CNode *node, CNode *parent)
{
   if (parent != NULL)
   {
	QPointF parentPos = parent->pos();
	int flag = m_tree->getState(parent->getPerson(), node->getPerson());
	QPointF pos;

    if (flag & BIT(etu::genealogy_tree::FATHER))
      pos = QPointF(parentPos.x()+DISTANCE,parentPos.y() -DISTANCE);
    if (flag & BIT(etu::genealogy_tree::MOTHER))
      pos = QPointF(parentPos.x()-DISTANCE,parentPos.y()-DISTANCE);
    if (flag & BIT(etu::genealogy_tree::CHILD))
      pos = QPointF(parentPos.x(),parentPos.y()+DISTANCE);

    node->setPos(pos);

    while (1)
    {
      QList<CNode* > nodes = getNodes(m_scene->collidingItems(node,Qt::IntersectsItemBoundingRect));

      if (nodes.length()>0)
      {
    	int x = pos.x();
    	x = (x>=100) ? x+DISTANCE : x-DISTANCE;
    	pos.setX(x);
		node->setPos(pos);
      }
      else
    	break;
    }

   }
   else node->setPos(QPoint(300,250));
    if (!m_scene->items().contains(node))
	  m_scene->addItem(node);

	++seqNumber;

    m_scene->clearSelection();
    node->setSelected(true);
    bringToFront();
    loadPhoto();
}

void CDiagram::loadPhoto()
{
  if (!m_dataSource->isPhotoExists(*(selectedNode()->getPerson())))
	return;

  Photo photo = *m_dataSource->getPhoto(*selectedNode()->getPerson());
  QPixmap* pixmap = new QPixmap();
  pixmap->loadFromData(photo.getData(),photo.getDataLength());
  updatePhoto(pixmap);
}

CNode *CDiagram::selectedNode() const
{
    QList<QGraphicsItem *> items = m_scene->selectedItems();
    if (items.count() == 1) {
        return dynamic_cast<CNode *>(items.first());
    } else {
        return 0;
    }
}

CLink *CDiagram::selectedLink() const
{
    QList<QGraphicsItem *> items = m_scene->selectedItems();
    if (items.count() == 1) {
        return dynamic_cast<CLink *>(items.first());
    } else {
        return 0;
    }
}

CDiagram::NodePair CDiagram::selectedNodePair() const
{
    QList<QGraphicsItem *> items = m_scene->selectedItems();
    if (items.count() == 2) {
        CNode *first = dynamic_cast<CNode *>(items.first());
        CNode *second = dynamic_cast<CNode *>(items.last());
        if (first && second)
            return NodePair(first, second);
    }
    return NodePair();
}

void CDiagram::createNewPerson(Person person , PersonType pType)
{
  try {
  Person* pers = new Person(person);
  CNode *node = new CNode(pers);

  switch(pType)
  {
    case etu::genealogy_tree::ROOT:
    	clear();
    	//if (m_tree == NULL)
    	  m_tree = new GenealogyTree();
    	m_tree->setRoot(pers);
        setupNode(node,selectedNode());
      break;
    case etu::genealogy_tree::FATHER:
        m_tree->setFather(selectedNode()->getPerson(),pers);
        createLink(selectedNode(), node);
        setupNode(node,selectedNode());
      break;
    case etu::genealogy_tree::MOTHER:
        m_tree->setMother(selectedNode()->getPerson(),pers);
        createLink(selectedNode(), node);
        setupNode(node,selectedNode());
      break;
    case etu::genealogy_tree::CHILD:
    	if (selectedNodePair() != NodePair())
    	{
    	  createChildNode(selectedNodePair().first, node);
    	  createChildNode(selectedNodePair().second, node);
      	  setupNode(node,selectedNodePair().first);
    	}
    	else
    	{
    	  createChildNode(selectedNode(), node);
    	  setupNode(node,selectedNode());
    	}
        break;
    case etu::genealogy_tree::MARRIED:
    	break;
    }
  }
  catch (genealogy_tree::GenealogyTreeException& ex) {
	  QMessageBox::critical(this,"",QString(tr(ex.what())));
  }
}

void CDiagram::createChildNode(CNode* parentNode, CNode* childNode)
{
  m_tree->addChild(parentNode->getPerson(), childNode->getPerson());
  createLink(parentNode, childNode);
}

void CDiagram::addFather()
{
	emit addFather(etu::genealogy_tree::FATHER);
}

void CDiagram::addMother()
{
	emit addMother(etu::genealogy_tree::MOTHER);
}

void CDiagram::addChild()
{
	emit addChild(etu::genealogy_tree::CHILD);
}

void CDiagram::removeDeletedPersons()
{
  foreach (CNode *node, getNodes(m_scene->items()))
    if (!m_tree->contains(node->getPerson())) // ==NULL ?
    	delete node;
}

QList<CNode* > CDiagram::getNodes(QList<QGraphicsItem* > items)
{
  QList<CNode* > nodes;

  foreach (QGraphicsItem *item, items)
  {
    CNode* node = dynamic_cast<CNode *>(item);
    if (node)
  	  nodes.push_back(node);
  }

  return nodes;
}

void CDiagram::updatePhoto(QPixmap* pixmap)
{
  selectedNode()->setPhoto(pixmap);
}

void CDiagram::search(Person* person)
{
  foreach(CNode *node, m_foundedNodes)
  node->setOutlineColor(NORMAL);
  m_foundedNodes.clear();

   std::list<const Person*>* list = m_tree->findAll(person);

   foreach (CNode *node, getNodes(m_scene->items()))
     if (std::find(list->begin(),list->end(),node->getPerson()) != list->end())
     {
    	 node->setOutlineColor(FOUND);
    	 m_foundedNodes.push_back(node);
     }
   //m_scene->setFocusItem(node);
   QString message(tr("Была найдена ")+QString::number(list->size())+tr(" персона"));
   QMessageBox::information(this, tr("Поиск"),message,QMessageBox::Ok);
}

void CDiagram::showSibs()
{
  try
  {
    if (selectedNode() != 0)
    {
      setTree(m_tree->getSibs(selectedNode()->getPerson()));
      m_scene->clear();
      replotTree();
    }
    else
	   QMessageBox::information(this, tr("Предупреждение"),tr("Cначала необходмо выделить персону"),QMessageBox::Ok);
  }
  catch (genealogy_tree::GenealogyTreeException& ex) {
	  QMessageBox::critical(this,"",QString(tr(ex.what())));
  }
}


}
}
